package org.amphiprion.gameengine;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import org.amphiprion.gameengine.util.ShaderHelper;
import org.amphiprion.gameengine.util.Texture;
import org.amphiprion.gameengine.util.TextureHelper;

import android.opengl.GLES20;
import android.opengl.Matrix;

public class Mesh implements IObject {
	/** How many bytes per float. */
	private final int mBytesPerFloat = 4;

	/** Size of the position data in elements. */
	private final int mPositionDataSize = 3;

	/** Size of the color data in elements. */
	private final int mColorDataSize = 4;

	/** Size of the normal data in elements. */
	private final int mNormalDataSize = 3;

	/** Size of the texture coordinate data in elements. */
	private final int mTextureCoordinateDataSize = 2;

	private FloatBuffer mVextexCoordinates;
	private FloatBuffer mVextexColors;
	private FloatBuffer mVextexNormals;
	private FloatBuffer mVextexTextureCoordinates;
	private int nbVertex;
	private String textureUri;

	public float Z;
	public float X;
	public float Y;
	public float rotXDegree;
	public float rotYDegree;
	public float rotZDegree;
	public float scale = 1;
	// public Vector3 rotationVector;
	// public float rotationDegree;

	private Texture texture;
	private Object tagObject;

	public Mesh(String name) {

	}

	public void setMeshData(float[] vertexCoordinates, float[] vertexColors, float[] vertexNormals, float[] vertexTextureCoordinates) {
		nbVertex = vertexCoordinates.length / 3;
		mVextexCoordinates = ByteBuffer.allocateDirect(vertexCoordinates.length * mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
		mVextexCoordinates.put(vertexCoordinates).position(0);

		mVextexColors = ByteBuffer.allocateDirect(vertexColors.length * mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
		mVextexColors.put(vertexColors).position(0);

		mVextexNormals = ByteBuffer.allocateDirect(vertexNormals.length * mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
		mVextexNormals.put(vertexNormals).position(0);

		mVextexTextureCoordinates = ByteBuffer.allocateDirect(vertexTextureCoordinates.length * mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
		mVextexTextureCoordinates.put(vertexTextureCoordinates).position(0);
	}

	public String getTextureUri() {
		return textureUri;
	}

	public void setTextureUri(String textureUri) {
		this.textureUri = textureUri;
	}

	private void loadGLTexture() {
		texture = TextureHelper.loadTexture(GameActivity.context, textureUri);
	}

	@Override
	public void draw(float[] mMVPMatrix, float[] mViewMatrix, float[] mModelMatrix, float[] mProjectionMatrix, float[] mLightPosInEyeSpace) {
		if (texture == null || texture.loaded == false) {
			loadGLTexture();
		}
		if (texture != null) {

			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture.textureId);
		}
		int mProgramHandle = ShaderHelper.getProgramHandle("DEFAULT");
		GLES20.glUseProgram(mProgramHandle);

		int mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
		int mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
		int mLightPosHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightPos");
		int mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");
		int mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
		int mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color");
		int mNormalHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Normal");
		int mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate");

		// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
		GLES20.glUniform1i(mTextureUniformHandle, 0);

		Matrix.setIdentityM(mModelMatrix, 0);
		Matrix.translateM(mModelMatrix, 0, X, Y, Z);
		// X .. Z .. Y
		Matrix.scaleM(mModelMatrix, 0, scale, scale, scale);
		Matrix.rotateM(mModelMatrix, 0, rotXDegree, 1.0f, 0.0f, 0.0f);
		Matrix.rotateM(mModelMatrix, 0, rotZDegree, 0.0f, 0.0f, 1.0f);
		Matrix.rotateM(mModelMatrix, 0, rotYDegree, 0.0f, 1.0f, 0.0f);
		// Matrix.rotateM(mModelMatrix, 0, rotationDegree, (float) rotationVector.x(), (float) rotationVector.y(), (float) rotationVector.z());
		// Pass in the position information
		mVextexCoordinates.position(0);
		GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, 0, mVextexCoordinates);

		GLES20.glEnableVertexAttribArray(mPositionHandle);

		// Pass in the color information
		mVextexColors.position(0);
		GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, 0, mVextexColors);

		GLES20.glEnableVertexAttribArray(mColorHandle);

		// Pass in the normal information
		mVextexNormals.position(0);
		GLES20.glVertexAttribPointer(mNormalHandle, mNormalDataSize, GLES20.GL_FLOAT, false, 0, mVextexNormals);

		GLES20.glEnableVertexAttribArray(mNormalHandle);

		// Pass in the texture coordinate information
		mVextexTextureCoordinates.position(0);
		GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 0, mVextexTextureCoordinates);

		GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);

		// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
		// (which currently contains model * view).
		Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);

		// Pass in the modelview matrix.
		GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);

		// This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
		// (which now contains model * view * projection).
		Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);

		// Pass in the combined matrix.
		GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		// Pass in the light position in eye space.
		GLES20.glUniform3f(mLightPosHandle, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1], mLightPosInEyeSpace[2]);

		// Draw the cube.
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, nbVertex);
	}

	@Override
	public float getZ() {
		return Z;
	}

	@Override
	public void setZ(float z) {
		this.Z = z;
	}

	@Override
	public float getX() {
		return X;
	}

	@Override
	public void setX(float x) {
		this.X = x;
	}

	@Override
	public float getY() {
		return Y;
	}

	@Override
	public void setY(float y) {
		this.Y = y;
	}

	public Object getTagObject() {
		return tagObject;
	}

	public void setTagObject(Object tagObject) {
		this.tagObject = tagObject;
	}

}
